
搜索文档...
文档首页
核心文档
快速起步
CLI 快速起步
开发指南
API参考手册
附加文档
教程
高级文档
动画
属性型指令
浏览器支持
组件样式
部署
多级注入器
响应式表单
HTTP 客户端
生命周期钩子
Angular模块 (NgModule)
npm 包
管道
路由与导航
安全
搭建剖析
结构型指令
测试
TypeScript 配置
从 AngularJS 升级
Webpack 简介
烹饪宝典
Angular for TypeScript
属性型指令
属性型指令把行为添加到现有元素上。
属性型指令用于改变一个 DOM 元素的外观或行为。

目录

指令概览
创建简单的属性型指令
应用属性型指令到模板中的元素
响应用户引发的事件
使用数据绑定把值传到指令中
绑定第二个属性
试试在线例子 / 可下载的例子。

指令概览

在 Angular 中有三种类型的指令：

组件 — 拥有模板的指令
结构型指令 — 通过添加和移除 DOM 元素改变 DOM 布局的指令
属性型指令 — 改变元素、组件或其它指令的外观和行为的指令。
组件是这三种指令中最常用的。 你在快速起步例子中第一次见到组件。

结构型指令修改视图的结构。例如，NgFor 和 NgIf。 要了解更多，参见结构型指令 guide。

属性型指令改变一个元素的外观或行为。例如，内置的 NgStyle 指令可以同时修改元素的多个样式。

创建一个简单的属性型指令

属性型指令至少需要一个带有@Directive装饰器的控制器类。该装饰器指定了一个用于标识属性的选择器。 控制器类实现了指令需要的指令行为。

本章展示了如何创建一个简单的属性型指令 myHighlight ，当用户把鼠标悬停在一个元素上时，改变它的背景色。你可以这样用它：

Copy Code
<p myHighlight>Highlight me!</p>
编写指令代码
按照开发环境的说明，创建一个名叫attribute-directives的项目文件夹。

在指定的文件夹下创建下列源码文件：

src/app/highlight.directive.ts
Copy Code
import { Directive, ElementRef, Input } from '@angular/core';
@Directive({ selector: '[myHighlight]' })
export class HighlightDirective {
    constructor(el: ElementRef) {
       el.nativeElement.style.backgroundColor = 'yellow';
    }
}
import语句指定了从 Angular 的core库导入的一些符号。

Directive提供@Directive装饰器功能。
ElementRef注入到指令构造函数中。这样代码就可以访问 DOM 元素了。
Input将数据从绑定表达式传达到指令中。
然后，@Directive装饰器函数以配置对象参数的形式，包含了指令的元数据。

@Directive装饰器需要一个 CSS 选择器，以便从模板中识别出关联到这个指令的 HTML。

用于 attribute 的 CSS 选择器就是属性名称加方括号。 这里，指令的选择器是[myHighlight]，Angular 将会在模板中找到所有带myHighlight属性的元素。

为什么不直接叫做 "highlight"？
尽管highlight 是一个比 myHighlight 更简洁的名字，而且它确实也能工作。 但是最佳实践是在选择器名字前面添加前缀，以确保它们不会与标准 HTML 属性冲突。 它同时减少了与第三方指令名字发生冲突的危险。
确认你没有给highlight指令添加ng前缀。 那个前缀属于 Angular，使用它可能导致难以诊断的 bug。例如，这个简短的前缀my可以帮助你区分自定义指令。
@Directive元数据之后就是该指令的控制器类，名叫HighlightDirective，它包含该指令的逻辑。 然后导出HighlightDirective，以便让它能从其它组件中访问到。

Angular 会为每个匹配的元素创建一个指令控制器类的实例，并把 Angular 的ElementRef和Renderer注入进构造函数。 ElementRef是一个服务，它赋予我们通过它的nativeElement属性直接访问 DOM 元素的能力。 Renderer服务允许通过代码设置元素的样式。

使用属性型指令

要使用这个新的HighlightDirective，创建一个模板，把这个指令作为属性应用到一个段落(p)元素上。 用 Angular 的话说，<p>元素就是这个属性型指令的宿主。

我们把这个模板放到它的app.component.html文件中，就像这样：

src/app/app.component.html
Copy Code
<h1>My First Attribute Directive</h1>
<p myHighlight>Highlight me!</p>
现在，在AppComponent中引用这个模板：

src/app/app.component.ts
Copy Code
import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html'
})
export class AppComponent {
  color: string;
}
接下来，添加了一个import语句来获得Highlight指令类，并把这个类添加到 NgModule 元数据的declarations数组中。 这样，当 Angular 在模板中遇到myHighlight时，就能认出这是指令了。

src/app/app.module.ts
Copy Code
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HighlightDirective } from './highlight.directive';
@NgModule({
  imports: [ BrowserModule ],
  declarations: [
    AppComponent,
    HighlightDirective
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }
运行应用，就会看到我们的指令确实高亮了段落中的文本。

First Highlight
你的指令没生效？
你记着设置@NgModule的declarations数组了吗？它很容易被忘掉。
打开浏览器调试工具的控制台，会看到像这样的错误信息：
Copy Code
EXCEPTION: Template parse errors:
  Can't bind to 'myHighlight' since it isn't a known property of 'p'.
Angular 检测到你正在尝试绑定到某些东西，但它不认识。所以它在declarations元数据数组中查找。 把HighlightDirective列在元数据的这个数组中，Angular 就会检查对应的导入语句，从而找到highlight.directive.ts，并了解myHightlight的功能。
总结：Angular 在<p>元素上发现了一个myHighlight属性。 然后它创建了一个HighlightDirective类的实例，并把所在元素的引用注入到了指令的构造函数中。 在构造函数中，我们把<p>元素的背景设置为了黄色。

响应用户引发的事件

当前，myHighlight只是简单的设置元素的颜色。 这个指令应该在用户鼠标悬浮一个元素时，设置它的颜色。

先把HostListener加进导入列表中，同时再添加Input符号，因为我们很快就要用到它。

Copy Code
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
然后使用HostListener装饰器添加两个事件处理器，它们会在鼠标进入或离开时进行响应。

Copy Code
  @HostListener('mouseenter') onMouseEnter() {
    this.highlight('yellow');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
@HostListener装饰器引用属性型指令的宿主元素，在这个例子中就是<p>。

当然，你可以通过标准的JavaScript方式手动给宿主 DOM 元素附加一个事件监听器。 但这种方法至少有三个问题：
必须正确的书写事件监听器。
当指令被销毁的时候，必须拆卸事件监听器，否则会导致内存泄露。
必须直接和 DOM API 打交道，应该避免这样做。
这些处理器委托给了一个辅助方法，它用于为DOM元素设置颜色，el就是你在构造器中声明和初始化过的。

src/app/highlight.directive.ts (constructor)
Copy Code
constructor(private el: ElementRef) { }
下面是修改后的指令代码：

src/app/highlight.directive.ts
Copy Code
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
  selector: '[myHighlight]'
})
export class HighlightDirective {
  constructor(private el: ElementRef) { }
  @HostListener('mouseenter') onMouseEnter() {
    this.highlight('yellow');
  }
  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }
  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
运行本应用并确认：当把鼠标移到p上的时候，背景色就出现了，而移开的时候，它消失了。

Second Highlight
使用数据绑定向指令传递值

现在的高亮颜色是硬编码在指令中的，这不够灵活。 我们应该让指令的使用者可以在模板中通过绑定来设置颜色。

我们先把highlightColor属性添加到指令类中，就像这样：

src/app/highlight.directive.ts (highlightColor)
Copy Code
@Input() highlightColor: string;
绑定到@Input属性
注意看@Input装饰器。它往类上添加了一些元数据，从而让该指令的highlightColor能用于绑定。

它之所以称为输入属性，是因为数据流是从绑定表达式流向指令内部的。 如果没有这个元数据，Angular就会拒绝绑定，参见稍后了解更多。

试试把下列指令绑定变量添加到AppComponent的模板中：

src/app/app.component.html (excerpt)
Copy Code
<p myHighlight highlightColor="yellow">Highlighted in yellow</p>
<p myHighlight [highlightColor]="'orange'">Highlighted in orange</p>
把color属性添加到AppComponent中：

src/app/app.component.ts (class)
Copy Code
export class AppComponent {
  color = 'yellow';
}
让它通过属性绑定来控制高亮颜色。

src/app/app.component.html (excerpt)
Copy Code
<p myHighlight [highlightColor]="color">Highlighted with parent component's color</p>
很不错，但还可以更好。我们可以在应用该指令时在同一个属性中设置颜色，就像这样：

Copy Code
<p [myHighlight]="color">Highlight me!</p>
[myHighlight]属性同时做了两件事：把这个高亮指令应用到了<p>元素上，并且通过属性绑定设置了该指令的高亮颜色。 我们复用了该指令的属性选择器[myHighlight]来同时完成它们。 这是清爽、简约的语法。

我们还要把该指令的highlightColor改名为myHighlight，因为它是颜色属性目前的绑定名。

src/app/highlight.directive.ts (renamed to match directive selector)
Copy Code
@Input() myHighlight: string;
这可不好。因为myHighlight是一个糟糕的属性名，而且不能反映该属性的意图。

绑定到@Input别名
幸运的是，我们可以随意命名该指令的属性，并且给它指定一个用于绑定的别名。

恢复原始属性名，并在@Input的参数中把选择器myHighlight指定为别名。

src/app/highlight.directive.ts (color property with alias)
Copy Code
@Input('myHighlight') highlightColor: string;
在指令内部，该属性叫highlightColor，在外部，当我们绑定到它时，它叫myHighlight。

这是最好的结果：理想的内部属性名，理想的绑定语法：

Copy Code
<p [myHighlight]="color">Highlight me!</p>
现在，我们绑定到了highlightColor属性，并修改onMouseEnter()方法来使用它。 如果有人忘了绑定到highlightColor，那就用红色进行高亮。

src/app/highlight.directive.ts (mouse enter)
Copy Code
@HostListener('mouseenter') onMouseEnter() {
  this.highlight(this.highlightColor || 'red');
}
这是最终版本的指令类。

src/app/highlight.directive.ts (excerpt)
Copy Code
import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[myHighlight]'
})
export class HighlightDirective {

  constructor(private el: ElementRef) { }

  @Input('myHighlight') highlightColor: string;

  @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.highlightColor || 'red');
  }

  @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}
写个测试程序试验下

凭空想象该指令如何工作可不容易。 在本节，我们将把AppComponent改成一个测试程序，它让你可以通过单选按钮来选取高亮颜色，并且把你选取的颜色绑定到指令中。

把app.component.html修改成这样：

Copy Code
<h1>My First Attribute Directive</h1>

<h4>Pick a highlight color</h4>
<div>
  <input type="radio" name="colors" (click)="color='lightgreen'">Green
  <input type="radio" name="colors" (click)="color='yellow'">Yellow
  <input type="radio" name="colors" (click)="color='cyan'">Cyan
</div>
<p [myHighlight]="color">Highlight me!</p>
修改AppComponent.color，让它不再有初始值。

Copy Code
export class AppComponent {
  color: string;
}
下面是测试程序和指令的动图。

Highlight v.2
绑定到第二个属性

本例的指令只有一个可定制属性，真实的应用通常需要更多。

目前，默认颜色（它在用户选取了高亮颜色之前一直有效）被硬编码为红色。我们要让模板的开发者也可以设置默认颜色。

把第二个名叫defaultColor的输入属性添加到HighlightDirective中：

src/app/highlight.directive.ts(defaultColor) (excerpt)
Copy Code
出错的文件: ../../../_fragments/attribute-directives/ts/src/app/highlight.directive.ts(defaultColor).md   所在路径: docs,ts,latest,guide,attribute-directives 文档路径: ../../../
修改该指令的onMouseEnter，让它首先尝试使用highlightColor进行高亮，然后用defaultColor，如果它们都没有指定，那就用红色作为后备。

Copy Code
@HostListener('mouseenter') onMouseEnter() {
  this.highlight(this.highlightColor || this.defaultColor || 'red');
}
当已经绑定过myHighlight属性时，要如何绑定到第二个属性呢？

像组件一样，你也可以绑定到指令的很多属性，只要把它们依次写在模板中就行了。 开发者可以绑定到AppComponent.color，并且用紫罗兰色作为默认颜色，代码如下：

Copy Code
<p [myHighlight]="color" defaultColor="violet">
  Highlight me too!
</p>
Angular之所以知道defaultColor绑定属于HighlightDirective，是因为我们已经通过@Input装饰器把它设置成了公共属性。

当这些代码完成时，测试程序工作时的动图如下：

Final Highlight
总结

本章介绍了如何：

构建一个属性型指令，它用于修改一个元素的行为。
把一个指令应用到模板中的某个元素上。
响应事件以改变指令的行为。
把值绑定到指令中。
最终的源码如下：

app/app.component.ts app/app.component.html app/highlight.directive.ts app/app.module.ts main.ts index.html
Copy Code
import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html'
})
export class AppComponent {
  color: string;
}
你还可以体验和下载在线例子 / 可下载的例子.

附录：为什么要加@Input？
在这个例子中hightlightColor是HighlightDirective的一个输入型属性。我们见过它没有用别名时的代码：

Copy Code
@Input() highlightColor: string;
也见过用别名时的代码：

Copy Code
@Input('myHighlight') highlightColor: string;
无论哪种方式，@Input装饰器都告诉Angular，该属性是公共的，并且能被父组件绑定。 如果没有@Input，Angular就会拒绝绑定到该属性。

但我们以前也曾经把模板HTML绑定到组件的属性，而且从来没有用过@Input。 差异何在？

差异在于信任度不同。 Angular把组件的模板看做从属于该组件的。 组件和它的模板默认会相互信任。 这也就是意味着，组件自己的模板可以绑定到组件的任意属性，无论是否使用了@Input装饰器。

但组件或指令不应该盲目的信任其它组件或指令。 因此组件或指令的属性默认是不能被绑定的。 从Angular绑定机制的角度来看，它们是私有的，而当添加了@Input时，它们变成了公共的 只有这样，它们才能被其它组件或属性绑定。

你可以根据属性名在绑定中出现的位置来判定是否要加@Input。

当它出现在等号右侧的模板表达式中时，它属于模板所在的组件，不需要@Input装饰器。
当它出现在等号左边的方括号（[ ]）中时，该属性属于其它组件或指令，它必须带有@Input 装饰器。
试用此原理分析下列范例：

Copy Code
<p [myHighlight]="color">Highlight me!</p>
color属性位于右侧的绑定表达式中，它属于模板所在的组件。 该模板和组件相互信任。因此color不需要@Input装饰器。
myHighlight属性位于左侧，它引用了MyHighlightDirective中一个带别名的属性，它不是模板所属组件的一部分，因此存在信任问题。 所以，该属性必须带@Input装饰器。
资源库

关于
书籍与培训
工具与库
社区
宣传资料
帮助

Stack Overflow
Gitter
Google Group
报告问题
网站反馈
社区

会议
Meetups
Twitter
GitHub
做贡献
其它语种

英文版
Powered by Google ©2010-2017。代码授权方式：MIT-style License。文档授权方式：CC BY 4.0。
本网站由洛阳永欣维护  豫ICP备16019859号-1